package cheme.ProcessEditor.Policies;

import java.util.List;

import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editpolicies.ComponentEditPolicy;
import org.eclipse.gef.requests.GroupRequest;

import cheme.ProcessModel.IBoundedTranslatable;
import cheme.ProcessModel.IParent;
import cheme.ProcessModel.ITranslatable;
import cheme.ProcessModel.Cmds.AddCmd;
import cheme.ProcessModel.Cmds.ChangeLocationCmd;
import cheme.ProcessModel.Cmds.DeleteCmd;
import cheme.ProcessModel.Cmds.OrphanTranslatableChildCmd;
import cheme.ProcessModel.Subprocess.ISubprocess;


public class SubprocessComponentPolicy extends ComponentEditPolicy
{
	XYLayout xyLayout;
	
	public SubprocessComponentPolicy(XYLayout xyLayout)
	{
		setXyLayout(xyLayout);
	}
	
	public XYLayout getXYLayout()
	{
		return xyLayout;
	}
	
	public void setXyLayout(XYLayout xyLayout)
	{
		this.xyLayout = xyLayout;
	}
	
	/*
	 * Note that this should really not remove the process units it contains from the model.
	 * Instead, it should move them up to the parent of this subprocess.
	 * 
	 * Phew!
	 * 
	 * @see org.eclipse.gef.editpolicies.ComponentEditPolicy#createDeleteCommand(org.eclipse.gef.requests.GroupRequest)
	 */
	protected Command createDeleteCommand(GroupRequest deleteRequest)
	{		
		/*
		 * Really, what this needs to do is build a series of OrphanChildCmd
		 * and then AddCmds.
		 */
		
		CompoundCommand result = new CompoundCommand("Delete Subprocess");//$NON-NLS-1$
		
		ISubprocess subprocess = ((ISubprocess)getHost().getModel());
		IParent parent = subprocess.getParent();
					
		/*
		 * Need to create an orphan command and a add command for each of the 
		 * process elements represented by these EditParts.
		 */
		

		List editParts = getHost().getChildren();
			
		/*
		 * Since the CreateGroupRequest is a GroupCommand that contains mulitple 
		 * EditParts I can simply pass it on to the existing 
		 * getOrphanChildrenCommand method in order to build a roper orphan
		 * command for all given model elements.
		 */
		
		result.add(createOrphanChildrenCmd(editParts));
		
		GraphicalEditPart parentPart = (GraphicalEditPart)getHost();
		GraphicalEditPart childPart;
		Rectangle r;
		Object constraint;
		
		/*
		 * The aggregation of the add commands will take more explicit management.
		 * Same basic process though. Loop over the edit parts generate add commands.
		 * One note is that proper bounds adjustment is required--this I kiped from
		 * the add command.
		 */

		for (int i = 0; i < editParts.size(); i++)
		{
			childPart = (GraphicalEditPart)editParts.get(i);
	
			/*
			 * Chain the add command new subprocess parent
			 */
			
			r = childPart.getFigure().getBounds().getCopy();
			parentPart.getFigure().translateToParent(r);
			
			constraint = getConstraintFor(r);

			result.add(createAddCmd(childPart, translateToModelConstraint(constraint), parent));
		}
		
		result.add(new DeleteCmd(subprocess));
		
		return result.unwrap();
	}
	
	protected Command createOrphanChildrenCmd(List editParts)
	{
		Command result = null;
		
		if(!editParts.isEmpty())
		{
			CompoundCommand compund = new CompoundCommand("Group Orphan");
			
			for (int i = 0; i < editParts.size(); i++)
			{
				OrphanTranslatableChildCmd orphan = new OrphanTranslatableChildCmd((IBoundedTranslatable)((EditPart)editParts.get(i)).getModel());
	
				compund.add(orphan);
			}
			
			result = compund.unwrap();
		}
		
		return result;
	}
	
	protected Command createAddCmd(EditPart childEditPart, Object constraint, IParent parent)
	{
		CompoundCommand result = new CompoundCommand("Group Add + ChangeBounds");
		
		ITranslatable childModel = (ITranslatable)childEditPart.getModel();
		
		AddCmd add = new AddCmd(childModel, parent);
			result.add(add);

		Rectangle rect = (Rectangle)constraint;
		ChangeLocationCmd changeBounds = new ChangeLocationCmd(childModel, rect.getLocation());
			result.add(changeBounds);
		
		return result.unwrap();
	}

	/**
	 * Returns a new Rectangle equivalent to the passed Rectangle.
	 * @param r the input Rectangle
	 * @return a copy of the input Rectangle
	 */
	public Object getConstraintFor(Rectangle r)
	{
		return new Rectangle(r);
	}

	/**
	 * Converts a constraint from the format used by LayoutManagers,
	 * to the form stored in the model.
	 * @param figureConstraint the draw2d constraint
	 * @return the model constraint
	 */
	protected Object translateToModelConstraint(Object figureConstraint)
	{
		return figureConstraint;
	}
}
